package com.leal.elasticsearch;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.indices.AliasDefinition;
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
import co.elastic.clients.elasticsearch.indices.GetAliasResponse;
import co.elastic.clients.elasticsearch.indices.IndexState;
import co.elastic.clients.elasticsearch.indices.get_alias.IndexAliases;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.apache.log4j.Logger;
import org.elasticsearch.client.RestClient;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author leal123
 * @projectName com.leal.client
 * @description: ESClient API
 * @date 2023/2/28 22:41
 */
public class ESClient {

    private static final Logger logger = Logger.getLogger(ESClient.class);
    public static final String LOCAL_ES = "127.0.0.1";
    public static final String HOME_ES = "192.169.52.10";

    public static ElasticsearchClient getClient(String mode) {

        // 2. 配置连接（无认证的本地ES，若有安全认证需配置CredentialsProvider）
        RestClient restClient = RestClient.builder(new HttpHost(LOCAL_ES, 9200)).build();

        // 3. 构建Transport和Client
        return new ElasticsearchClient(
                new RestClientTransport(
                        restClient, new JacksonJsonpMapper())
        );
    }

    public static void closeClient(ElasticsearchClient client) {
        try {
            if (client != null) {
                client._transport().close();
            }
        } catch (Exception e) {
            logger.error("关闭ES客户端出错", e);
        }
    }

    public static boolean createSimpleIndex(ElasticsearchClient client, String indexName) throws IOException {
        CreateIndexResponse createIndexResponse = client.indices().create(
                c -> c.index(indexName)
        );
        return createIndexResponse.acknowledged();
    }

    /**
     * 判断索引是否存在
     *
     * @param client    ElasticsearchClient客户端
     * @param indexName 索引名称
     * @return
     * @throws IOException
     */
    public static boolean isExistIndex(ElasticsearchClient client, String indexName) throws IOException {
        return client.indices().exists(
                c -> c.index(indexName)
        ).value();
    }

    /**
     * 删除索引
     *
     * @param client    ElasticsearchClient客户端
     * @param indexName 、索引名称
     * @return
     * @throws IOException
     */
    public static boolean deleteIndex(ElasticsearchClient client, String indexName) throws IOException {
        if (!isExistIndex(client, indexName)) {
            logger.error("索引不存在");
            return false;
        }
        return client.indices()
                .delete(c -> c.index(indexName))
                .acknowledged();
    }

    /**
     * 添加别名
     *
     * @param client    ElasticsearchClient客户端
     * @param indexName 索引名称
     * @param aliasName 别名名称
     * @return
     * @throws IOException
     */
    public static boolean addAlias(ElasticsearchClient client, String indexName, String aliasName) throws IOException {
        if (!isExistIndex(client, indexName)) {
            logger.error("索引不存在");
            return false;
        }
        // 修复点：正确使用函数式构建器来构造别名操作
        return client.indices()
                .updateAliases(up -> up
                        .actions(act -> act
                                .add(add -> add
                                        .index(indexName)
                                        .alias(aliasName)
                                )
                        )
                ).acknowledged();
    }


    /**
     * 删除别名
     *
     * @param client    ElasticsearchClient客户端
     * @param indexName 索引名称
     * @param aliasName 别名名称
     * @return
     * @throws IOException
     */
    public static boolean removeAlias(ElasticsearchClient client, String indexName, String aliasName) throws IOException {
        if (!isExistIndex(client, indexName)) {
            logger.error("索引不存在");
            return false;
        }
        return client.indices()
                .updateAliases(up -> up
                        .actions(act -> act
                                .remove(remove -> remove
                                        .index(indexName)
                                        .alias(aliasName)
                                )
                        )
                ).acknowledged();

    }

    /**
     * 获取某个索引下的所有别名，生产默认只有一个
     *
     * @param client    ElasticsearchClient客户端
     * @param indexName 索引名称
     * @return
     * @throws IOException
     */
    public static HashSet<String> getAliasForIndex(ElasticsearchClient client, String indexName) throws IOException {
        GetAliasResponse response = client.indices()
                .getAlias(g -> g.index(indexName));

        HashSet<String> aliasNames = new HashSet<>();
        Map<String, AliasDefinition> aliases = response.result().get(indexName).aliases();
        if (aliases != null) {
            aliasNames.addAll(aliases.keySet());
        }
        logger.info("索引 " + indexName + "别名列表：" + aliasNames);
        return aliasNames;
    }

    /**
     * 获取某个别名下的所有索引
     *
     * @param client    ElasticsearchClient客户端
     * @param aliasName 别名名称
     * @ return 索引名称列表
     */
    public static HashSet<String> getIndexForAlias(ElasticsearchClient client, String aliasName) throws IOException {
        GetAliasResponse response = client.indices()
                .getAlias(g -> g.name(aliasName));
        HashSet<String> indexNames = new HashSet<>();
        for (Map.Entry<String, IndexAliases> entry : response.result().entrySet()) {
            indexNames.add(entry.getKey());
        }
        logger.info("别名 " + aliasName + "索引列表：" + indexNames);
        return indexNames;
    }

    /**
     * 检查指定索引是否包含指定别名
     * @param client    ElasticsearchClient客户端
     * @param indexName 索引名称
     * @param aliasName  别名名称
     */
    public static boolean isExistsAliasForIndex(ElasticsearchClient client, String indexName, String aliasName) throws IOException {
        return client.indices()
                .existsAlias(e -> e.index(indexName)
                        .name(aliasName))
                .value();
    }
    /**
     * 清理旧索引，保留最近n天的索引
     * @param client    ElasticsearchClient客户端
     * @param aliasName 别名名称
     * @param keepDays   保留天数
     */
    public static void cleanOldIndex(ElasticsearchClient client, String aliasName, int keepDays) throws IOException {
        // 同类索引为 别名 + 时间（8位）
        //先获取所有索引
        Set<String> indexes = client.indices().get(g -> g.index(aliasName + "*"))
                .result().keySet();
        /*
        过滤出特定的索引
        1.匹配出索引的日期排序
        2.排除掉含有别名
         */
        List<String> indexList = indexes.stream()
                .filter(index -> index.matches(aliasName + "\\d{8}"))
                .filter(index -> {
                    try {
                        return !isExistsAliasForIndex(client, index, aliasName);
                    } catch (Exception e) {
                        return false;
                    }
                })
                .sorted()
                .collect(Collectors.toList());

        List<String> needDeleteIndex = indexList.subList(0, Math.max(0, indexList.size() - keepDays));
        for (String index : needDeleteIndex) {
            boolean flag = deleteIndex(client, index);
            if (flag) {
                logger.info("删除索引 " + index + "成功");
            } else {
                logger.error("删除索引 " + index + "失败");
            }
        }
    }

}
